package com.xmlt.core.template;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;

import com.xmlt.core.template.generation.GenerationTemplate;
import com.xmlt.core.template.instance.Instance;
import com.xmlt.core.template.instance.InstanceFactory;
import com.xmlt.core.template.resolver.IDefaultTemplateDescriptor;
import com.xmlt.core.template.resolver.IResolver;

public class TemplateManager {

	private Map<String, Template> templatesMap = new HashMap<String, Template>();
	private Map<String, Instance> instancesMap = new HashMap<String, Instance>();
	private Map<String, List<Instance>> typedInstanceMap = new HashMap<String, List<Instance>>();
	private IResolver resolver;

	public TemplateManager(IResolver res) {
		resolver = res;
		for(IDefaultTemplateDescriptor idtd: res.getDefaultTemplateDescriptors()) {
			try {
				buildTemplate(idtd.getReference(), idtd.getDocument());
			} catch (TemplateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private Template buildTemplate(String reference, Document d) throws TemplateException {
		Document doc = d;
		if (doc == null)
			doc = resolver.find(reference);
		if (doc == null)
			throw new TemplateException("Could not find a document at '" + reference + "'");
		Template t = new Template(doc, reference, this);
		templatesMap.put(reference, t);
		
		// refresh instances
		List<Instance> instances = typedInstanceMap.get(reference);
		if (instances != null) {
			for (Instance i: instances) {
				i.setTemplate(t);
			}
		}
		return t;
	}

	public Template findTemplate(String reference) throws TemplateException {
		return findTemplate(reference, false);
	}
	
	public Template findTemplate(String reference, boolean reload) throws TemplateException {
		if (!reload && templatesMap.containsKey(reference)) {
			return templatesMap.get(reference);
		}
		return buildTemplate(reference, null);
	}
	
	public void removeTemplate(String ref) {
		templatesMap.put(ref, null);
	}

	public Instance loadInstance(String path) throws TemplateException {
		if (instancesMap.containsKey(path)) {
			return instancesMap.get(path);
		}
		
		String topLevelInstancePath = path;
		if (path.indexOf("#") > -1) {
			topLevelInstancePath = path.substring(0, path.indexOf("#"));
		}
		Document doc = resolver.find(topLevelInstancePath);
		if (doc == null)
			throw new TemplateException("Could not find a document at '" + topLevelInstancePath + "'");
		Instance instance = (Instance) InstanceFactory.newInstance(doc, this, topLevelInstancePath);
		instancesMap.put(topLevelInstancePath, instance);
		Template linkedTemplate = instance.getTemplate();
		if (linkedTemplate != null) {
			List<Instance> typedInstances = typedInstanceMap.get(linkedTemplate.getReference());
			if (typedInstances == null) {
				typedInstances = new ArrayList<Instance>();
				typedInstanceMap.put(linkedTemplate.getReference(), typedInstances);
			}
			typedInstances.add(instance);
		}
		return instancesMap.get(path);
	}
	
	public void registerInstance(Instance instance) {
		instancesMap.put(instance.getReference(), instance);
		Template linkedTemplate = instance.getTemplate();
		if (linkedTemplate != null) {
			List<Instance> typedInstances = typedInstanceMap.get(linkedTemplate.getReference());
			if (typedInstances == null) {
				typedInstances = new ArrayList<Instance>();
				typedInstanceMap.put(linkedTemplate.getReference(), typedInstances);
			}
			typedInstances.add(instance);
		}
	}
	
	public IResolver getResolver() {
		return resolver;
	}
	
	public Map<String, Template> getAllTemplates() {
		return templatesMap;
	}

	public void removeInstance(String ref) {
		Instance instance = instancesMap.get(ref);
		if (instance == null)
			return;
		
		instancesMap.remove(ref);
		typedInstanceMap.remove(ref);
	}
	
	public List<Instance> getInstances(String ref) {
		return typedInstanceMap.get(ref);
	}
	
	public GenerationTemplate findGenerationTemplate(String ref) throws TemplateException {
		return new GenerationTemplate(ref, this);
	}

}
